package saves.objects;

import game.Units;
import images.ImageInfo;
import images.ObjectLoader;
import images.ShipLoader;
import java.awt.Color;
import math.Point;
import org.jbox2d.collision.shapes.PolygonShape;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.BodyType;
import org.jbox2d.dynamics.Fixture;
import org.jbox2d.dynamics.World;
import saves.Area;
import saves.EntidadeNave;
import saves.EntidadeNave.DamageType;

public class Nave extends Corpo {

    private static final long serialVersionUID = 5901434419084018927L;
    public static final float ANG_VEL = 10;
    
    private EntidadeNave entidade;
    private transient int rotation, acc;
    
    private Laser laser; //the laser this ship is emmiting, if any
    
    private transient int delayTime;
    
    public enum ActionResults {
        OK, OUT_OF_EENRGY, OUT_OF_PROJECTILES, ON_COOLDOWN, SHIP_DISABLED;
    }
    
    protected Nave() {}
    
    public Nave(float x, float y, EntidadeNave nave, World world) {
        super(x, y, -1, BodyType.DYNAMIC, world);
        this.entidade = nave;
        this.laser = null;
        
        //shape
        PolygonShape ps = new PolygonShape();
        float s = Units.getRealSize(this.getPixelWidth());
        float wh = entidade.getWingHeight();
        Vec2[] vec = new Vec2[] {new Vec2(s/2, 0), new Vec2(wh*s, -s/2), new Vec2(-s/2, -s/6), new Vec2(-s/2, s/6), new Vec2(wh*s, s/2)};
        //Vec2[] vec = new Vec2[] {new Vec2(s, s/2), new Vec2(s/2 + wh*s, 0), new Vec2(0, s/2 - s/6), new Vec2(0, s/2 + s/6), new Vec2(s/2 + wh*s, s)};
        ps.set(vec, vec.length);
        Fixture f = this.body.createFixture(ps, 2);
        f.setDensity(.001f);
        f.setFriction(0);
        f.setRestitution(.01f);
        //setupShape();
        
        //damping
        this.body.setLinearDamping(0.02f);
    }
    
    /**
     * Sets up the shape of the ship (fixture) given the current equip set.
     */
    public final void setupShape() {
        PolygonShape ps = new PolygonShape();
        float s = Units.getRealSize(this.getPixelWidth());
        float wh = entidade.getWingHeight();
        Vec2[] vec = new Vec2[] {new Vec2(s/2, 0), new Vec2(wh*s, -s/2), new Vec2(-s/2, -s/6), new Vec2(-s/2, s/6), new Vec2(wh*s, s/2)};
        //Vec2[] vec = new Vec2[] {new Vec2(s, s/2), new Vec2(s/2 + wh*s, 0), new Vec2(0, s/2 - s/6), new Vec2(0, s/2 + s/6), new Vec2(s/2 + wh*s, s)};
        ps.set(vec, vec.length);
        this.body.getFixtureList().m_shape = ps;
    }

    public EntidadeNave getEntidade() {
        return entidade;
    }
    
    public static float getLinearModifier(float x) {
        if (x <= 0) //do not apply (min cap)
            return 0;
        if (x > 255) //max cap
            x = 255;
        //.007f -> slow     2
        //.0075f -> normal  6
        //.01f -> f1        30
        //.0115f -> fastest 100
        return (float) (2.7867431225827904E-04*Math.pow(x, .5) + 4.9804655849604888E-03*Math.pow(x, -.5) - 2.0397575826911450E-02*Math.pow(x, -2) + 5.5851286072712159E-03*(Math.atan(x)))*4/5;
    }
    
    public static float getAngularModifier(float x) {
        if (x <= 0) //do not apply (min cap)
            return 0;
        if (x > 255) //max cap
            x = 255;
        //.005f -> slow     2
        //.01f -> normal    6
        //.011f -> f1       30
        //.012f -> fastest  100
        return (float) (1.7739883927584617E-04*Math.pow(x, .5) - 6.7704727310764889E-03*Math.pow(x, -.5) + 1.6633413430493890E-02*Math.pow(x, -2) + 6.6641579937035920E-03*(Math.atan(x)))*4/5;
    }
    
    @Override
    public void update(int delta, Area area) {
        if (this.getEntidade().destroy()) {
            this.setDestruir();
            area.addExplosion(new Vec2(x, y), .2f, null);
            return;
        }
        if (this.getEntidade().isDisabled()) {
            laser = null;
            return;
        }
        
        if (this.delayTime > 0)
            this.delayTime -= delta;
        float rotSpeed = getAngularModifier(entidade.getAttributeModifier(EntidadeNave.Status.SPEED.p(), 2)), linearSpeed = getLinearModifier(entidade.getAttributeModifier(EntidadeNave.Status.SPEED.p(), 1));
        if (rotation != 0 && (acc == 0 || entidade.consumir(-.05f)) && (acc != 0 || entidade.consumir(.01f))) {
            int sign = rotation / Math.abs(rotation);
            this.body.setAngularVelocity((float) Math.toRadians(sign * delta * rotSpeed));
        } else
            this.body.setAngularVelocity(0);
        
        if (acc > 0) {
            if (entidade.consumir(.1f))
                this.setForca(linearSpeed);
        } else if (acc < 0) { //brakes
            if (this.getVelocidade() != 0) {
                float cx = this.body.getLinearVelocity().x, cy = this.body.getLinearVelocity().y;
                this.body.setLinearVelocity(new Vec2(Math.abs(cx) < 1 ? 0 : (cx + (cx > 0 ? -1 : 1)), Math.abs(cy) < 1 ? 0 : (cy + (cy > 0 ? -1 : 1))));
            }
        }
        
        this.entidade.update(delta);
        
        if (laser != null) {
            if (!entidade.consumir(laser.getEnergyCost()))
                laser = null;
            else {
                laser.move(getRealX(), getRealY());
                laser.rotate(this.getRealAngle());
                laser.update(delta, area);
            }
        }
        
        super.update(delta, area); //just in case
    }

    public void setAcc(int acc) {
        this.acc = acc;
    }
    
    public void setRotation(int rotation) {
        this.rotation = rotation;
    }
    
    public void setForca(float f) {
        //this.body.applyLinearImpulse(new Vec2(f * (float) Math.cos(this.getRealAngle()), f * (float) Math.sin(this.getRealAngle())), new Vec2(getRealX(), getRealY()));
        this.body.applyForceToCenter(new Vec2(10 * f * (float) Math.cos(this.getRealAngle()), 10 * f * (float) Math.sin(this.getRealAngle())));
    }
    
    public float getVelocidade() {
        return this.body.getLinearVelocity().length();
    }
    
    public boolean inRange(Corpo m) {
        return new Point(m.getPixelX(), m.getPixelY()).dist(new Point(this.getPixelX(), this.getPixelY())) <= EntidadeNave.DEFAULT_PERCEPTION_DISTANCE;
    }
    
    public int getDelayTime() {
        return this.delayTime;
    }
    
    public void setDelayTime(int delayTime) {
        this.delayTime = delayTime;
    }
    
    public ActionResults atirarProjetil(int type, Area a) {
        float potency = 1;
        if (Projetil.HAS_COOLDOWN[type]) {
            if (this.getDelayTime() > 0)
                return ActionResults.ON_COOLDOWN;
            else
                this.setDelayTime(Projetil.COOLDOWN);
        }
        
        ActionResults r = entidade.fire(type, Projetil.ENERGY_COSTS[type]);
        if (r == ActionResults.OK) {
            //encontrar o x,y de saída
            Point p = ShipLoader.getPontoDisparo(entidade, type, this.getDrawingAngle());
            float ang = this.getRealAngle() % (float) (2*Math.PI);
            Projetil pr = new Projetil(this.getRealX() + Units.getRealSize((int) p.x - ShipLoader.DEFAULT_SHIP_SIZE/2), this.getRealY() + Units.getRealSize((int) p.y - ShipLoader.DEFAULT_SHIP_SIZE/2), ang, type, potency, this.getEntidade(), a.getWorld());
            a.addObjeto(pr);
        }
        return r;
    }
    
    public void atirarLaser(int type) {
        if (laser == null || !laser.is(type)) {
            float potency = 1;
            Point p = ShipLoader.getPontoDisparo(entidade, type, this.getDrawingAngle());
            this.laser = new Laser(new Vec2(p.x, p.y), this.getRealAngle(), type, potency, entidade);
        } else
            laser = null;
    }
    
    public int getState() {
        if (getEntidade().isDisabled())
            return 0;
        if (rotation < 0)
            return acc > 0 ? 5 : 2;
        if (rotation > 0)
            return acc > 0 ? 4 : 1;
        //not rotating
        return acc == 0 ? 0 : 3;
    }
    
    @Override
    public void harm(float power, DamageType type, EntidadeNave owner) {
        super.harm(power, type, owner);
        boolean t = owner != null && !this.getEntidade().destroy();
        entidade.causarDano(7.5f*power, type);
        if (t && this.getEntidade().destroy()) //died due to this attack
            owner.ganharExp(this.getEntidade().getXpWorth());
    }
    
    @Override
    public final int getPixelWidth() {
        return ShipLoader.DEFAULT_SHIP_SIZE;
    }
    
    @Override
    public final int getPixelHeight() {
        return ShipLoader.DEFAULT_SHIP_SIZE;
    }

    @Override
    public void draw(int dx, int dy) {
        int ox = getPixelX() - dx, oy = getPixelY() - dy;
        if (laser != null)
            laser.draw(dx, dy);
        ShipLoader.bindTexture();
        ShipLoader.draw(entidade, ox - getPixelWidth()/2, oy - getPixelHeight()/2, 64, getDrawingAngle(), getState());
        ObjectLoader.bindObjects(); //volta para textura anterior
    }
    
    @Override
    public void drawSelector(int ox, int oy) {
        ImageInfo.drawOctagon(ox - this.getPixelWidth()/2 - 5, oy - this.getPixelHeight()/2 - 5, this.getPixelWidth() + 10, this.getPixelHeight() + 10, Color.WHITE, 10);
    }
    
    public void drawBounds(int x, int y) {
        float a = (float) Math.toDegrees(this.getRealAngle());
        ImageInfo.rotate(a, x, y);
        Vec2[] vs = ((PolygonShape) this.body.getFixtureList().getShape()).getVertices();
        for (Vec2 v : vs)
            ImageInfo.drawColorSquare(x + Units.getPixelSize(v.x) - 2, y + Units.getPixelSize(v.y) - 2, 4, 4, Color.BLUE);
        ImageInfo.rotate(-a, x, y);
    }
}
